package com.kaibes.core.spring.proxy;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import com.kaibes.core.base.proxy.ProxyHandler;
import com.kaibes.core.base.proxy.ProxyInvokeListener;
import com.kaibes.core.base.util.TypeUtils;
import com.kaibes.core.spring.ApplicationUtils;

@Component
public class ProxyBeanPostProcessor implements BeanPostProcessor {

	@Value("${kaibes.code.spring.proxy:com.kaibes}")
    private List<String> proxyList = new ArrayList<>();
	@Autowired
    private ApplicationUtils applicationUtils;
	private Map<Type, Object> proxyMap = new HashMap<>();
	
	private void postBefore(Object bean, String beanName) throws ClassNotFoundException, IllegalAccessException {
		String beanClassName = bean.getClass().getName();
		boolean flag = false;
        for (String proxy : proxyList) {
            if (beanClassName.startsWith(proxy)) {
                flag = true;
                break;
            }
        }
        if (flag) {
        	ApplicationContext applicationContext = applicationUtils.getApplicationContext();
            if (beanClassName.contains("$$")) {
                beanClassName = beanClassName.substring(0, beanClassName.indexOf("$"));
            }
            // bean class
            Class<?> clazz = Class.forName(beanClassName, true, bean.getClass().getClassLoader());
            // 所有的field
            Field[] fields = clazz.getDeclaredFields();
            // 遍历field
            for (Field field : fields) {
                try {
                    field.setAccessible(true);
                    
                    Type type = field.getGenericType();
                    Object proxyBean = proxyMap.get(type);
                    if (proxyBean != null) {
                        field.set(bean, proxyBean);
                    } else if (field.isAnnotationPresent(Proxy.class)) {
                        Proxy proxyA = field.getDeclaredAnnotation(Proxy.class);
                        // 已经存在的域
                        Object oldField = field.get(bean);
                        ProxyHandler<Object> handler;
                        if (oldField == null) {
                            // 域为NULL
                            handler = applicationUtils.getProxyHandler(type);
                            if (handler == null && proxyA.value()) {
                                throw new RuntimeException("在类"+beanClassName+"中，代理的域"+field.getName()+"不存在，程序被中断了！");
                            }
                        } else {
                            // 域存在
                            handler = ProxyHandler.getNewInstance(oldField);
                        }
                        if (proxyA.listener() != ProxyInvokeListener.class) {
                            ProxyInvokeListener listener = applicationContext.getBean(proxyA.listener());
                            handler.setInvokelistener(listener);
                        } else {
                            Collection<ProxyInvokeListener4Spring> listeners = applicationContext.getBeansOfType(ProxyInvokeListener4Spring.class).values();
                            for (ProxyInvokeListener4Spring listener : listeners) {
                                Type targetType = listener.getTargetType().getType();
                                if (targetType == type || TypeUtils.getClass(targetType).isAssignableFrom(TypeUtils.getClass(type))) {
                                    handler.setInvokelistener(listener);
                                    break;
                                }
                            }
                        }
                        if (handler != null) {
                            proxyBean = handler.getProxy();
                            proxyMap.put(type, proxyBean);
                            field.set(bean, proxyBean);
                        } else {
                            proxyMap.put(type, null);
                        }
                    }
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                }
            }
        }
	}
	
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		try {
			postBefore(bean, beanName);
			return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
		} catch (ClassNotFoundException | IllegalAccessException e1) {
			return new BeanDefinitionStoreException(beanName, e1);
		}
	}
	
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
	}

}
